java序列化和反序列化
Updated:
参考好文:
http://www.cnblogs.com/xiohao/p/4234184.html
http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html
《Thinking in java》
一.什么是Java对象序列化
  Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
  使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
  除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用,在本文的后续章节中将会陆续讲到。
二.Java序列化的作用
  有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象。例如,有的时候我们想要把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要自己去通过java把相应的对象写成转换成字节流。对于这种通用的操作,我们为什么不使用统一的格式呢?没错,这里就出现了java的序列化的概念。
  在Java的OutputStream类下面的子类ObjectOutputStream类就有对应的WriteObject(Object object) 其中要求对应的object实现了java的序列化的接口。
  为了更好的理解java序列化的应用,我举两个自己在开发项目中遇到的例子:
      1)在使用tomcat开发JavaEE相关项目的时候,我们关闭tomcat后,相应的session中的对象就存储在了硬盘上,如果我们想要在tomcat重启的
时候能够从tomcat上面读取对应session中的内容,那么保存在session中的内容就必须实现相关的序列化操作。
      2)如果我们使用的java对象要在分布式中使用或者在rmi远程调用的网络中使用的话,那么相关的对象必须实现java序列化接口。
三.实现java对象的序列化和反序列化。
Java对象的序列化有两种方式。
- a.是相应的对象实现了序列化接口Serializable,这个使用的比较多,对于序列化接口Serializable接口它的主要作用就是标识这个对象时可序列化的,
 
注意:
java中的序列化时transient变量(这个关键字的作用就是告知JAVA我不可以被序列化)和静态变量不会被序列化。
静态变量不属于对象,属于类。不能被序列化。还有瞬态的变量也不能被序列化 。
序列化保存的时对象的状态,静态变量属于类的状态,所以序列化并不保存静态变量。
具体是什么个样子,后面会解释的
每个枚举类型都会默认继承类java.lang.Enum,而该类实现了Serializable接口,所以枚举类型对象都是默认可以被序列化的。
Serializable的作用
  为什么一个类实现了Serializable接口,它就可以被序列化呢?在上节的示例中,使用ObjectOutputStream来持久化对象,在该类中有如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
201.	private void writeObject0(Object obj, boolean unshared) throws IOException {  
2.	      ...
3.	    if (obj instanceof String) {  
4.	        writeString((String) obj, unshared);  
5.	    } else if (cl.isArray()) {  
6.	        writeArray(obj, desc, unshared);  
7.	    } else if (obj instanceof Enum) {  
8.	        writeEnum((Enum) obj, desc, unshared);  
9.	    } else if (obj instanceof Serializable) {  
10.	        writeOrdinaryObject(obj, desc, unshared);  
11.	    } else {  
12.	        if (extendedDebugInfo) {  
13.	            throw new NotSerializableException(cl.getName() + "\n" 
14.	                    + debugInfoStack.toString());  
15.	        } else {  
16.	            throw new NotSerializableException(cl.getName());  
17.	        }  
18.	    }  
19.	    ...  
20.	}
从上述代码可知,如果被写对象的类型是String,或数组,或Enum,或Serializable,那么就可以对该对象进行序列化,否则将抛出NotSerializableException。补充:基本类型的包装类的对象,所有容器类的对象甚至Class对象都可以被序列化。
1  | package com.shop.domain;  | 
b.实现序列化的第二种方式为实现接口Externalizable,该接口是继承于Serializable接口的
并增加了两个方法:
1
2void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
  首先,我们在序列化对象的时候,由于这个类实现了Externalizable 接口,在writeExternal()方法里定义了哪些属性可以序列化,哪些不可以序列化,所以,对象在经过这里就把规定能被序列化的序列化保存文件,不能序列化的不处理,然后在反序列的时候自动调用readExternal()方法,根据序列顺序挨个读取进行反序列,并自动封装成对象返回,然后在测试类接收,就完成了反序列。
     所以说Exterinable的是Serializable的一个扩展。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123package yu;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.Scanner;
/*
 * 测试实体类
 */
class Person implements Externalizable{
	private static final long serialVersionUID = 123L;
	
	String userName;
	String password;
	String age;
	
	public Person(){
		System.out.println(“无参构造器”);
	}
	
	public Person(String userName, String password, String age){
		this.userName = userName;
        this.password = password;
        this.age = age;
		System.out.println(“有参构造器”);
	}
	
	public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
	
    /**
     * 序列化操作的扩展类
     */
    
	public void writeExternal(ObjectOutput out) throws IOException {
		// TODO Auto-generated method stub
    	//不泄露年龄
		out.writeObject(userName);
		out.writeObject(password);
	}
    
    /**
     * 序列化操作的扩展类
     */
	
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		//注意这里的接受顺序是有限制的哦,否则的话会出错的
        // 例如上面先write的是A对象的话,那么下面先接受的也一定是A对象...
		userName = (String)in.readObject();
		password = (String)in.readObject();
		
	}
	
	public String toString(){
		//注意这里的年龄是不会被序列化的,所以在反序列化的时候是读取不到数据的
		return "用户名:" + userName + " 密码:" + password + " 年龄:" + age;
	}
}
//序列化和反序列化的相关操作类
class Operate{
	
	public Operate(){
		
	}
	
	//序列化方法
	public void serializable(Person person) throws FileNotFoundException, IOException{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("k:/rot/ceshi.txt"));
		oos.writeObject(person);
		oos.close();
	}
	//反序列化方法
	public Person deSerializable() throws FileNotFoundException, IOException, ClassNotFoundException{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("k:/rot/ceshi.txt"));
		Person temp = (Person)ois.readObject();
		ois.close();
		return temp;
		
	}
}
//测试实体主类
public class lian {
	
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{ 
		
		Operate operate = new Operate();
		Person person = new Person("dabai", "123456", "18");
		System.out.println("为序列化之前的相关数据如下:\n" + person.toString());
		operate.serializable(person);
		Person newPerson = operate.deSerializable();
		System.out.println("-------------------------------------------------------");
		System.out.println("序列化之后的相关数据如下:\n" + newPerson.toString());
	} 
}

  首先,我们在序列化UserInfo对象的时候,由于这个类实现了Externalizable 接口,序列化的细节需要由程序员去完成。在writeExternal()方法里定义了哪些属性可以序列化,哪些不可以序列化,所以,对象在经过这里就把规定能被序列化的序列化保存文件,不能序列化的不处理,然后在反序列的时候自动调用readExternal()方法,根据序列顺序挨个读取进行反序列,并自动封装成对象返回,然后在测试类接收,就完成了反序列。
使用Externalizable进行序列化时,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。这就是为什么在此次序列化过程中Person类的无参构造器会被调用。由于这个原因,实现Externalizable接口的类必须要提供一个无参的构造器,且它的访问权限为public。
- java中的序列化时transient变量(这个关键字的作用就是告知JAVA我不可以被序列化)和静态变量不会被序列
 
1  | package yu;  | 

  注意这里 count是类的,访问的时候是通过类来访问的没有参与对象的序列化
静态成员属于类级别的,所以不能序列化这里的不能序列化的意思,是序列化信息中不包含这个静态成员域这个测试成功,是因为都在同一个机器(而且是同一个进程),因为我这里的jvm已经把count加载进来了,所以获取的是加载好的count,如果是传到另一台机器或者关掉程序重写写个程序读入test.obj,此时因为别的机器或新的进程是重新加载count的,所以count信息就是初始时的信息。
所以再写一个类来测试,就知道答案了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package yu;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Lian3 {
	
	public static void main(String args[]){
        
        try {
 
            FileInputStream fis = new FileInputStream("k:/rot/ceshi.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
 
            Student1 s3 = (Student1) ois.readObject();
            Student1 s4 = (Student1) ois.readObject();
 
            System.out.println(s3);
            System.out.println(s4);
 
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
    }
}
证明了,静态成员不会被序列化
补充:
如果你先序列化对象A后序列化B,那么在反序列化的时候一定记着JAVA规定先读到的对象是先被序列化的对象,
最好显示声明一个serialVersionUID,如果源代码中稍微改了一点点小地方,如果没有显示声明他,默认用算法算出的将是两个完全不同的数,反序列化的时候就会失败。
是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。
日后更新:
三.实现序列化的其它方式
    1)是把对象包装成JSON字符串传输。
     2)采用谷歌的ProtoBuf
参考:    http://www.cnblogs.com/xiohao/p/4234184.html
四、单例模式下的序列化
参考:http://developer.51cto.com/art/201202/317181.htm